package channels4java.messages;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;

import channels4java.exceptions.MessageCreationException;
import channels4java.exceptions.Channels4JavaException.ErrorType;

import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;

public class Message
{
	public static enum MessageType
	{
		DATA, // the message only transports application data inside the channel
		HANDSHAKE, // message to check if the remote host also is running an instance of the library
	}
	
	public static enum CommonParameter
	{
		PAYLOAD, // the data to be transported
		
		CERTIFICATE, // used to pass a certificate in base64 encoding
		SESSION_REJECTED, // used to indicate that a session being initiated in an handshake is rejected
	}
	
	private MessageType type;
	
	private String[] keys;
	private String[] values;
	private transient HashMap<String, String> parameters;
	
	public Message(MessageType type)
	{
		if(type == null) throw new IllegalArgumentException("invalid message type");
		
		this.type = type;
	}
	
	public Message(String serializedMessage) throws MessageCreationException
	{
		Message msg;
		
		try
		{
			msg = new Gson().fromJson(serializedMessage, Message.class);
		}
		catch(JsonSyntaxException e)
		{
			throw new MessageCreationException(ErrorType.INVALID_MESSAGE_FORMAT, "the serialized message has an invalid format (maybe it's not part of the library)", e);
		}
		
		this.type = msg.getMessageType();
		
		this.parameters = new HashMap<String, String>();
		for(int i = 0; i < msg.keys.length; i++)
		{
			this.parameters.put(msg.keys[i], msg.values[i]);
		}
	}
	
	public MessageType getMessageType()
	{
		return this.type;
	}
	
	public int getParametersCount()
	{
		if(this.parameters == null) return 0;
		else return this.parameters.size();
	}
	
	public void addParameter(CommonParameter key, String value)
	{
		this.addParameter(key.toString(), value);
	}
	
	public void addParameter(String key, String value)
	{
		if(key == null || value == null) throw new IllegalArgumentException("invalid parameter arguments");
		
		if(this.parameters == null) this.parameters = new HashMap<String, String>();
		this.parameters.put(key, value);
	}
	
	public boolean containsParameter(CommonParameter key)
	{
		return this.containsParameter(key.toString());
	}
	
	public boolean containsParameter(String key)
	{
		if(this.parameters == null) return false;
		else return this.parameters.containsKey(key);
	}
	
	public String getParameterValue(CommonParameter key)
	{
		return this.getParameterValue(key.toString());
	}
	
	public String getParameterValue(String key)
	{
		if(key == null) throw new IllegalArgumentException("invalid parameter");
		if(this.parameters == null) return null;
		
		if(this.parameters.containsKey(key)) return this.parameters.get(key);
		else return null;
	}
	
	@Override
	public String toString()
	{
		this.keys = new String[this.parameters.size()];
		this.values = new String[this.parameters.size()];
		
		ArrayList<Entry<String, String>> entries = new ArrayList<Entry<String, String>>(this.parameters.entrySet());
		for(int i = 0; i < this.parameters.size(); i++)
		{
			this.keys[i] = entries.get(i).getKey();
			this.values[i] = entries.get(i).getValue();
		}
		
		return new Gson().toJson(this, Message.class);
	}
}
